import { isObj, isEle, isText } from "../utils.js";


const ATTRS = 'ATTRS';

const TEXT = 'TEXT';

const REMOVE = 'REMOVE';

const REPLACE = 'REPLACE';

/**
 *  patches
 *   {
 *      type: REPLACE,
 *      newVnode
 *   }
 *
 *   {
 *      type: ATTRS,
 *      attrs: {
 *      }
 *   }
 *
 *   {
 *     type: TEXT,
 *     text: ''
 *   }
 *
 *   {
 *     type: REMOVE,
 *
 *   }
 *
 *
 *
*/


class VNode {
    constructor(type,  attrs, children) {
        this.type = type;
        this.attrs = attrs;
        this.children = children;
    }
}

function createElement(type, attrs, children){

    return new VNode(type, attrs, children)
}

export function render(vnode){

    var el = document.createElement(vnode.tag);

    if(typeof vnode.attrs === 'object'){
        if(vnode.attrs !== null){


            for(let attr in vnode.attrs){
                setAttrs(el, attr, vnode.attrs[attr]);
            }

        }
    }

    if(Array.isArray(vnode.children) && vnode.children.length > 0){
        renderVnodeChildren(el, vnode.children);
    }

    return el;
}


function setAttrs(ele, attr, value){

    switch(attr){
        case ele.tagName === 'INPUT' || ele.tagName === 'TEXTAREA':
            ele.value = value;
            break;
        case 'style':
            if(typeof value === 'string'){
                ele.style.cssText = value;
            }else if(typeof value === 'object'){
                for(let i in value){
                    ele.style[i] = value[i];
                }
            }
            break;
        default:
            ele.setAttribute(attr, value);
            break;
    }
}


function renderVnodeChildren( ele, vnodeChildren){

    vnodeChildren.forEach(c => {
        if(c instanceof VNode){
            ele.appendChild(render(c));
        }else if(typeof c === 'string'){
            ele.appendChild(document.createTextNode(c));
        }
    });
}



function renderDOM(rootEle, renderEle){
    rootEle.appendChild(renderEle);
}


// 以上实现了 虚拟dom 渲染成真实dom的过程


// 编写 diff

export function diffVnode(oldVnode, newVnode){
    let patches = {};

    let index = 0;

    vnodeWalk(oldVnode, newVnode, index, patches);



    return patches;

}

function vnodeWalk(o, n, i, globalPatches){
    let vnPatches = [];

    if(!o){
        vnPatches.push({
            type: REMOVE,
            index: i
        })
    }else if(typeof o === 'string' && typeof n === 'string'){
        // 说明子都是字符串
        if(o !== n){
            vnPatches.push({
                type: TEXT,
                text: n
            })
        }
    }else if(o.tag === n.tag){
        // 说明标签相同 于是对比属性
        let attrsPatches = vnodeAttrsWalk(o.attrs, n.attrs);

        if(Object.keys(attrsPatches).length){
            vnPatches.push({
                type: ATTRS,
                attrs: attrsPatches
            })
        }
        vnodeChildrenWalk(o.children, n.children, i, globalPatches)
    }else{
        // 说明是 替换了
        vnPatches.push({
            type: REPLACE,
            newVnode : n
        })
    }

    if(vnPatches.length > 0){
        globalPatches[i] = vnPatches;
    }
}


function vnodeAttrsWalk(oAttrs, nAttrs){
    let attrsPatches = {};
    if(isObj(oAttrs) && isObj(nAttrs)){

        for(let i in oAttrs){

            if(nAttrs[i] !== oAttrs[i]){
                // 说明 属性更改了
                if(nAttrs[i]){
                    attrsPatches[i] = nAttrs[i];
                }
            }
        }

        for(let i in nAttrs){
            if(!oAttrs[i]) {
                // 说明 原来没有这个属性， 于是添加这个属性

                attrsPatches[i] = nAttrs[i]
            }
        }
    }

    return attrsPatches;
}


function vnodeChildrenWalk(oChild, nChild, i, globalPatches){

    oChild.forEach((c,idx) => {
        vnodeWalk(c, nChild[idx], ++i, globalPatches)
    })
}


function doPatche(rnode, patches){
    let rnIndex = 1;
    rnodeWalk(rnode, patches, rnIndex);
}

function rnodeWalk(rnode, finalPatches, rnIndex){
    const rnPatche = finalPatches[rnIndex++]; // 取到对应的patche


    const children = rnode.childNodes ;

    [...children].forEach(i => rnodeWalk(i, finalPatches, rnIndex++));
    if(rnPatche){
        // 存在 说明当前的节点需要更新补丁
        if(isText(rnode) && rnode.textContent.trim()) {
            updatePatches2RealDom(rnode, rnPatche)
        }
    }
}


// 更新补丁到 真实dom上
function updatePatches2RealDom(rnode, patche) {

    for(let i=0,l=patche.length; i<l; i++){
        let item = patche[i];
        switch(item.type){
            case ATTRS:
                for(let i in item.attrs){

                    let val = item.attrs[i];
                    if(val){
                        setAttrs(rnode, i, val)
                    }
                }
                break;
            case TEXT:
                rnode.textContent = item.text;
                break;
            case REPLACE:
                let nDom = null;
                if(item.newVnode instanceof VNode){
                    // VNODE
                    nDom = render(item.newVnode);
                }else{
                    // text
                    nDom = document.createTextNode(item.newVnode);
                }
                rnode.parentNode.replaceChild(nDom, rnode);
                break;
            case REMOVE:
                rnode.parentNode.removeChild(rnode);
                break;

            default:
                break;

        }

    }

}

export function updateRealDomByPatches(rnode, patches){
    if(isEle(rnode) && isObj(patches)) {
        doPatche(rnode, patches)
    }
}




